package it.luigiarpino.alt2jsim.core;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class ExportToNeptuneXMLFormat
{

	private Grafo grafo;
	private RandomAccessFile fileOut;
	private String description;
	private String ram;
	private String interfaceBindings;
	private String osName;
	private String osPassword;
	private String template;
	private Long ritardo;
	private boolean evaluatePropDelay;
	private Long bandwidth;
	private Double loss;
	private Double duplicated;
	private Double corrupted;
	
	List<String>[] interfacce;
	
	private static String[] hexCyphers = new String[] { "0", "1", "2", "3",
		"4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" };
	private static String[] hexSecondByteAllowedCyphers = new String[] { "2",
		"6", "A", "E" };
	
	
	public ExportToNeptuneXMLFormat(Grafo g)
	{
		this.grafo=g;
	}
	
	public void scriviFile(String nomeFile, String descrizione, String ram, String interfaceBindings,
			String osName, String osPassword, String template, Long ritardo, boolean evaluatePropDelay, Long banda, Double loss,
			Double duplicated, Double corrupted)
	{
		this.description=descrizione;
		this.ram=ram;
		this.interfaceBindings=interfaceBindings;
		this.osName=osName;
		this.osPassword=osPassword;
		this.template=template;
		this.ritardo=ritardo;
		this.evaluatePropDelay=evaluatePropDelay;
		this.bandwidth=banda;
		this.loss=loss;
		this.duplicated=duplicated;
		this.corrupted=corrupted;
        try
        {
               File f=new File(nomeFile);
               if (f.exists())
               {
            	   f.delete();
               }
               this.fileOut=new RandomAccessFile(f,"rw");
               this.writeNetwork();
               fileOut.close();
        }
        catch(java.lang.Exception e)
        {
        	e.printStackTrace();
            System.out.println("Unable to write file "+nomeFile);
            System.exit(0);
        }
	}
	
	private void writeNetwork() throws IOException
	{  
		String idTopologia=String.valueOf( System.currentTimeMillis() );
		fileOut.writeBytes("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\r\n");
		fileOut.writeBytes("<!-- This file was generated by Alt2Jsim\r\n");
		fileOut.writeBytes("     Alt2jsim was developed by Luigi Arpino luigiarpino@yahoo.it -->\r\n");
		fileOut.writeBytes("<topology description='"+description+"' id='"+idTopologia+"'>\r\n");
		this.writeNodes();
		this.writeLinks();
		fileOut.writeBytes("</topology>\r\n");
	}
	
	private void writeNodes() throws IOException
	{
		// Calcolo le interfaccie presenti in ogni nodo
		this.interfacce= new List[this.grafo.getNumOfNode()]; 
		for(int i=0 ; i< this.grafo.getNumOfNode(); i++)
		{
			this.interfacce[i]=new LinkedList<String>();
			int numInterface = this.grafo.getGradoOfNode(i);
			for(int j=1; j<=numInterface; j++)
			{
				this.interfacce[i].add("eth"+j);
			}
		}
		fileOut.writeBytes("\r\n");
		fileOut.writeBytes("\t<nodes>\r\n");
		for(int i=0 ; i< this.grafo.getNumOfNode(); i++)
		{
			String id=String.valueOf( i );
			String desc=this.grafo.getNameOfNode(i);
			int numInterface = this.grafo.getGradoOfNode(i);
			this.writeNode(id, desc, this.ram, numInterface, this.interfaceBindings, this.osName,
				this.osPassword, this.template);
		}
		fileOut.writeBytes("\t</nodes>\r\n");
		fileOut.writeBytes("\r\n");
	}
	
	/**
	 * 
	 * @param id
	 * @param description
	 * @param ram
	 * @param numInterface
	 * @param interfaceBindings
	 * @param osName
	 * @param osPassword
	 * @param template
	 * @throws IOException
	 */
	private void writeNode(String id, String description, String ram, int numInterface,
			String interfaceBindings, String osName, String osPassword, String template) throws IOException
	{
		fileOut.writeBytes("\r\n");
		fileOut.writeBytes("\t\t<node id='"+id+"'>\r\n");
		fileOut.writeBytes("\t\t<description>"+description+"</description>\r\n");
		fileOut.writeBytes("\t\t<name/>\r\n");
		fileOut.writeBytes("\t\t<cpu amount='0' clock='0' name='' type=''/>\r\n");
		fileOut.writeBytes("\t\t<ram dimension='"+ram+"'/>\r\n");
		fileOut.writeBytes("\t\t<hd dimension='2.0'/>\r\n");
	
		fileOut.writeBytes("\t\t<network_interfaces>\r\n");
		// Interfaccia di controllo
		fileOut.writeBytes("\t\t\t<interface MAC='"+getRandomMAC()+"' name='eth0' type=''>\r\n");
		fileOut.writeBytes("\t\t\t<interface_binding type='"+interfaceBindings+"'/>\r\n");
		fileOut.writeBytes("\t\t\t</interface>\r\n");
		for (int i=1; i<=numInterface; i++)
		{
			fileOut.writeBytes("\t\t\t<interface MAC='"+getRandomMAC()+"' name='eth"+i+"' type=''>\r\n");
			fileOut.writeBytes("\t\t\t<interface_binding type='"+interfaceBindings+"'/>\r\n");
			fileOut.writeBytes("\t\t\t</interface>\r\n");
		}
		fileOut.writeBytes("\t\t</network_interfaces>\r\n");
		
		fileOut.writeBytes("\t\t<operative_system name='"+osName+"' password='"+osPassword+"' username='root' version='4'/>\r\n");
		
		fileOut.writeBytes("\t\t<software_configuration template='"+template+"'>\r\n");
		fileOut.writeBytes("\t\t\t<sshServer port='22'/>\r\n");
		fileOut.writeBytes("\t\t\t<software/>\r\n");
		fileOut.writeBytes("\t\t</software_configuration>\r\n");
		
		fileOut.writeBytes("\t\t<hypervisor name='' version=''/>\r\n");
		fileOut.writeBytes("\t\t</node>\r\n");
		fileOut.writeBytes("\r\n");
	}

	
	private void writeLink(String idLink, String idNodeSource, String nicSource,
			String idNodeDest, String nicDest, String linkDelay) throws IOException
	{
		fileOut.writeBytes("\r\n");
		fileOut.writeBytes("\t\t<link id='"+idLink+"' type='fullDuplex'>\r\n");
		
		fileOut.writeBytes("\t\t<end_point nodeId='"+idNodeSource+"' type='source'>\r\n");
		fileOut.writeBytes("\t\t\t<multiplexing tech='OLPI'/>\r\n");
		fileOut.writeBytes("\t\t\t<interface name='"+nicSource+"'/>\r\n");
		fileOut.writeBytes("\t\t</end_point>\r\n");
		
		fileOut.writeBytes("\t\t<end_point nodeId='"+idNodeDest+"' type='destination'>\r\n");
		fileOut.writeBytes("\t\t\t<multiplexing tech='OLPI'/>\r\n");
		fileOut.writeBytes("\t\t\t<interface name='"+nicDest+"'/>\r\n");
		fileOut.writeBytes("\t\t</end_point>\r\n");
		
		fileOut.writeBytes("\t\t<property name='bandwidth' value='"+this.bandwidth+"kbps'/>\r\n");
		fileOut.writeBytes("\t\t<property name='delay' value='"+linkDelay+"ms'/>\r\n");
		fileOut.writeBytes("\t\t<property name='loss' value='"+this.loss+"%'/>\r\n");
		fileOut.writeBytes("\t\t<property name='duplicate' value='"+this.duplicated+"%'/>\r\n");
		fileOut.writeBytes("\t\t<property name='corrupt' value='"+this.corrupted+"%'/>\r\n");
		fileOut.writeBytes("\t\t</link>\r\n");
		fileOut.writeBytes("\r\n");
	}
	
	
	private void writeLinks() throws IOException
	{
		fileOut.writeBytes("\t<links>\r\n");
		int numLink=this.grafo.getNumOfLink()/2;
		System.out.println("numLink="+numLink);
		for(int i=0 ; i< numLink; i++)
		{
			String idLink="link"+i;
			int idSource=this.grafo.getSourceNodeOfLink(i);
			String idNodeSource=String.valueOf( idSource );
			String nicSource= this.interfacce[idSource].remove(0);
			int idDest=this.grafo.getDestNodeOfLink(i);
			String idNodeDest=String.valueOf( idDest );
			String nicDest=this.interfacce[idDest].remove(0);
			// calcolo il delay
			String linkDelay;
			if (this.evaluatePropDelay)
			{
				int ritardo=(int) (this.ritardo/this.grafo.getScale()*this.grafo.getLenghtOfLink(i));
				linkDelay = String.valueOf(ritardo);
			}
			else
			{
				linkDelay = String.valueOf( this.ritardo );
			}
			this.writeLink(idLink, idNodeSource, nicSource, idNodeDest, nicDest, linkDelay);
		}
		fileOut.writeBytes("\t</links>\r\n");
		fileOut.writeBytes("\r\n");
	}
	
	/**
	 * Genera un MAC address casuale, senza verificarne l'unicita'.
	 * 
	 * @return
	 */
	public static String getRandomMAC()
	{

		String[] address = new String[12];
		Random rand = new Random();
		for (int i = 0; i < address.length; i++) {
			int index = rand.nextInt(hexCyphers.length);

			address[i] = hexCyphers[index];
		}

		int index = rand.nextInt(hexSecondByteAllowedCyphers.length);
		address[1] = hexSecondByteAllowedCyphers[index];

		String composedAddress = "";
		for (int i = 0; i < address.length; i++) {
			composedAddress += address[i];
			if (i % 2 != 0 && i != address.length - 1)
				composedAddress += ":";
		}
		return composedAddress;
	}
	
}
